package zsoft.rest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import com.atlassian.plugins.rest.common.security.AnonymousAllowed;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.regexp.REProgram;

import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.bc.project.ProjectService.GetProjectResult;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.search.SearchException;
import com.atlassian.jira.jql.builder.JqlClauseBuilder;
import com.atlassian.jira.jql.builder.JqlQueryBuilder;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.jira.project.version.VersionManager;
import com.atlassian.jira.project.version.Version;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.util.velocity.VelocityRequestContextFactory;
import com.atlassian.jira.web.bean.PagerFilter;


/**
 * A resource of message.
 */
@Path("/message")
public class MyRestResource {

	
	private final VersionManager versionManager;
	private final ProjectManager projectManager;
    private final JiraAuthenticationContext authenticationContext;
    private final SearchService searchService;
    private VelocityRequestContextFactory velocityRequestContextFactory;
    private final IssueManager issueManager;
    private final UserManager userManager;
    
    private Date today;
    
	//private final ProjectManager projectManager;
	public MyRestResource(final SearchService searchService, final JiraAuthenticationContext authenticationContext, final VelocityRequestContextFactory velocityRequestContextFactory, final VersionManager versionManager, final ProjectManager projectManager, final IssueManager issueManager, final UserManager userManager)
    {
        this.searchService = searchService;
        this.authenticationContext = authenticationContext;
        this.velocityRequestContextFactory = velocityRequestContextFactory;
        this.versionManager = versionManager;
        this.projectManager = projectManager;
        this.issueManager = issueManager;
        this.userManager = userManager;
        
       
    }
 
	
//    @SuppressWarnings("deprecation")
//	@GET
//    @AnonymousAllowed
//    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
//    public Response getMessage()
//    {
//    	//List<GenericValue> projects = new ArrayList<GenericValue>();
//    	//projects.addAll(projectManager.getProjectObjByName(arg0).getProjects());
//    	
//    	
//    	
//    	return Response.ok(new MyRestResourceModel(projectManager.getProjectByKey("FP").toString())).build();
//    }
    
    /**
     * Retrieve individual hours allocation chart (IHAC) for one user, user is specified by user id
     * @param projectIdString
     * @return
     */
    @GET
    @Path ("/getUserIHAC")
    public Response getVersionsForProject(
    		@QueryParam ("userId") String userIdString, 
    		@QueryParam ("mode") String mode, 
    		@QueryParam ("numberOfTimePeriods") String numberOfTimePeriods,
    		@QueryParam ("issueTypes") String issueTypes,
    		@QueryParam ("defaultRemainingTime") String defaultRemainingTime)
    {
    	long startTime=System.currentTimeMillis();
    	//performance information collected
    	String performanceInfo="";
    	try{
    		
    		//default parameters
        	if(userIdString==null || userManager.getUser(userIdString)==null){
        		IndividualHoursReport ihr = new IndividualHoursReport(userIdString, new ArrayList<ProjectReport>(),"User '"+userIdString+"' cannot be found","");
        		return Response.ok(ihr).build();
        	}
        	if(mode==null || (! (mode.equals("byDay")) && ! (mode.equals("byWeek")))){
        		mode = "byDay";
        	}
        	try{
        		if(numberOfTimePeriods==null || numberOfTimePeriods.equals("") || new Long(numberOfTimePeriods)<=0 || new Long(numberOfTimePeriods)>1000){
            		numberOfTimePeriods="64";
            	}
        	}
        	catch (NumberFormatException e) {
    			numberOfTimePeriods="64";
    		}
        	
        	ArrayList<String> issueTypesList = new ArrayList<String>();
        	if(issueTypes==null || issueTypes.equals("")){
        		//wild card for all issue types
        		issueTypes="";
        	}
        	else{
        		for(String type: issueTypes.split(",")){
        			issueTypesList.add(type.trim());
        		}
        	}
        	
        	if(defaultRemainingTime==null || defaultRemainingTime.equals("")){
        		defaultRemainingTime="1.0";
        	}
        	
        	Double defaultRemainingTimeDouble = new Double(defaultRemainingTime);
        	
        	//Collect all open jira issues for the user
        	User user = userManager.getUser(userIdString);
        	
        	Long startSearch = System.currentTimeMillis();
        	//Get all issues for that one user
            // The search interface requires JQL clause... so let's build one
            JqlClauseBuilder jqlClauseBuilder = JqlQueryBuilder.newClauseBuilder();
            com.atlassian.query.Query query = jqlClauseBuilder
            	.assigneeUser(userIdString).and()
            	.status("Open","In Progress","Re-opened")
            	.buildQuery();
            // A page filter is used to provide pagination. Let's use an unlimited filter to
            // to bypass pagination.
            PagerFilter pagerFilter = PagerFilter.getUnlimitedFilter();
            com.atlassian.jira.issue.search.SearchResults searchResults = null;
            try {
                // Perform search results
                searchResults = searchService.search(user, query, pagerFilter);
            } catch (SearchException e) {
                e.printStackTrace();
            }
            List<Issue> issues = searchResults.getIssues();
            performanceInfo+="\nSearch in ms: "+(System.currentTimeMillis()-startSearch);
            
            //Performance improvement: generate bins for issues for each project
            HashMap<String,ArrayList<Issue>> projectBins = new HashMap<String, ArrayList<Issue>>();
            for(Issue i : issues){
            	String p = i.getProjectObject().getKey();
            	if(projectBins.containsKey(p)){
            		projectBins.get(p).add(i);
            	}
            	else{
            		ArrayList<Issue> issueList = new ArrayList<Issue>();
            		issueList.add(i);
            		projectBins.put(p, issueList);
            	}
            }
            
            //Generate list of projects
            //ArrayList<String> projectNames = getAllProjectNames(issues);
            
            //generate JSON structure (by day, week)
            IndividualHoursReport ihr = new IndividualHoursReport(user.getDisplayName(), new ArrayList<ProjectReport>(),null,"");
            IndividualHoursReport ihrByWeek = new IndividualHoursReport(user.getDisplayName(), new ArrayList<ProjectReport>(),null,"");
            
            
            
            for(String projectName : projectBins.keySet()){
            	Double remainingEstimateByProjectByWeek = 0.0;
            	ArrayList<TimePeriodReportDetail> reportDetailsWeek = new ArrayList<TimePeriodReportDetail>();
            	Long startProject = System.currentTimeMillis();
            	for(long j=-1L;j<new Long(numberOfTimePeriods);j++){
            		
            		Date currentDate = new Date(today().getTime()+1000*60*60*24*j);
            		ArrayList<TimePeriodReportDetail> reportDetailsDay = new ArrayList<TimePeriodReportDetail>();
            		Double remainingEstimateByProjectByDay = computeRemainingEstimateByDay(projectName,currentDate,projectBins.get(projectName),issueTypesList,defaultRemainingTimeDouble, reportDetailsDay);
            		
            		//Start of the week = Monday: start counting hours
            		if(currentDate.getDay()==1 || currentDate.equals(today())){
            			remainingEstimateByProjectByWeek = remainingEstimateByProjectByDay;
            			reportDetailsWeek = new ArrayList<MyRestResource.TimePeriodReportDetail>();
            			//copy daily details
            			for(TimePeriodReportDetail tprd : reportDetailsDay){
            				TimePeriodReportDetail newTprp = new TimePeriodReportDetail(
            						tprd.getJiraIssueKey(),
            						tprd.getDueDate(),
            						tprd.getFixVersion(),
            						tprd.getAllocatedHours(),
            						tprd.getTotalAllocatedHours(),
            						tprd.getSummary());
            				reportDetailsWeek.add(newTprp);
            			}
            		}
            		else{
            			remainingEstimateByProjectByWeek += remainingEstimateByProjectByDay;
            			//look if issue already present, and if so add hours
            			for(TimePeriodReportDetail dDay : reportDetailsDay){
            				boolean found = false;
            				for(TimePeriodReportDetail dWeek : reportDetailsWeek){
            					if(dWeek.getJiraIssueKey().equals(dDay.getJiraIssueKey())){
            						found=true;
            						dWeek.addAllocatedHours(dDay.getAllocatedHours());
            						break;
            					}
            				}
            				if(!found){
            					reportDetailsWeek.add(dDay);
            				}
            			}
            		}
            		
            		//daily report
            		TimePeriodReport tpr = new TimePeriodReport(
            				remainingEstimateByProjectByDay,
            				formatDate(currentDate), 
            				hasDeadline(projectName,currentDate),
            				getDeadline(projectName,currentDate),
            				(currentDate.getDay()==0||currentDate.getDay()==6)?true:false
            				,reportDetailsDay);
            		//generate weekly report on Sunday or if the date is yesterday
            		if(currentDate.getDay()==0 || currentDate.before(today())){
            			TimePeriodReport tprWeek = new TimePeriodReport(
                				remainingEstimateByProjectByWeek,
                				formatDateWeek(currentDate), 
                				hasDeadlineInWeek(projectName,currentDate),
                				getDeadlineInWeek(projectName,currentDate),
                				false,
                				reportDetailsWeek		);
            			//get or generate project report
                		ProjectReport prWeek = null;
                		for(ProjectReport temp : ihrByWeek.getProjectReports()){
                			if(temp.getProjectName().equals(projectName)){
                				prWeek = temp;
                				break;
                			}
                		}
                		if(prWeek==null){
                			prWeek = new ProjectReport(projectName, new ArrayList<VersionReport>());
                			ihrByWeek.getProjectReports().add(prWeek);
                		}
                		
                		//get or generate version report
                		//currently version is the same as project
                		VersionReport vrWeek = null;
                		for(VersionReport temp : prWeek.getVersionReports()){
                			if(temp.getVersionName().equals(projectName)){
                				vrWeek = temp;
                				break;
                			}
                		}
                		if(vrWeek==null){
                			vrWeek = new VersionReport(projectName, new ArrayList<TimePeriodReport>());
                			prWeek.getVersionReports().add(vrWeek);
                		}
                		
                		vrWeek.getTimePeriodReports().add(tprWeek);
            			
            		}
            		
            		//get or generate project report
            		ProjectReport pr = null;
            		for(ProjectReport temp : ihr.getProjectReports()){
            			if(temp.getProjectName().equals(projectName)){
            				pr = temp;
            				break;
            			}
            		}
            		if(pr==null){
            			pr = new ProjectReport(projectName, new ArrayList<VersionReport>());
            			ihr.getProjectReports().add(pr);
            		}
            		
            		//get or generate version report
            		//currently version is the same as project
            		VersionReport vr = null;
            		for(VersionReport temp : pr.getVersionReports()){
            			if(temp.getVersionName().equals(projectName)){
            				vr = temp;
            				break;
            			}
            		}
            		if(vr==null){
            			vr = new VersionReport(projectName, new ArrayList<TimePeriodReport>());
            			pr.getVersionReports().add(vr);
            		}
            		
            		vr.getTimePeriodReports().add(tpr);
            	}
            	
            	//generate report for unscheduled tasks
            	ArrayList<TimePeriodReportDetail> reportDetailsDay = new ArrayList<TimePeriodReportDetail>();
        		Double remainingEstimateByProjectByDay = computeRemainingEstimateByDay(projectName,null,projectBins.get(projectName),issueTypesList,defaultRemainingTimeDouble,reportDetailsDay);
        		
        		//daily report
        		TimePeriodReport tpr = new TimePeriodReport(
        				remainingEstimateByProjectByDay,
        				"unscheduled", 
        				false,
        				null,
        				false,
        				reportDetailsDay);
        		//generate weekly report on Sunday or if the date is yesterday
        		TimePeriodReport tprWeek = new TimePeriodReport(
        				remainingEstimateByProjectByDay,
            			"unscheduled", 
            			false,
            			null,
            			false,
            			reportDetailsDay);
    			//get or generate project report
        		ProjectReport prWeek = null;
        		for(ProjectReport temp : ihrByWeek.getProjectReports()){
        			if(temp.getProjectName().equals(projectName)){
        				prWeek = temp;
        				break;
        			}
        		}
        		if(prWeek==null){
        			prWeek = new ProjectReport(projectName, new ArrayList<VersionReport>());
        			ihrByWeek.getProjectReports().add(prWeek);
        		}
        		
        		//get or generate version report
        		//currently version is the same as project
        		VersionReport vrWeek = null;
        		for(VersionReport temp : prWeek.getVersionReports()){
        			if(temp.getVersionName().equals(projectName)){
        				vrWeek = temp;
        				break;
        			}
        		}
        		if(vrWeek==null){
        			vrWeek = new VersionReport(projectName, new ArrayList<TimePeriodReport>());
        			prWeek.getVersionReports().add(vrWeek);
        		}
            		
            	vrWeek.getTimePeriodReports().add(tprWeek);
        			
        		//get or generate project report
        		ProjectReport pr = null;
        		for(ProjectReport temp : ihr.getProjectReports()){
        			if(temp.getProjectName().equals(projectName)){
        				pr = temp;
        				break;
        			}
        		}
        		if(pr==null){
        			pr = new ProjectReport(projectName, new ArrayList<VersionReport>());
        			ihr.getProjectReports().add(pr);
        		}
        		
        		//get or generate version report
        		//currently version is the same as project
        		VersionReport vr = null;
        		for(VersionReport temp : pr.getVersionReports()){
        			if(temp.getVersionName().equals(projectName)){
        				vr = temp;
        				break;
        			}
        		}
        		if(vr==null){
        			vr = new VersionReport(projectName, new ArrayList<TimePeriodReport>());
        			pr.getVersionReports().add(vr);
        		}
        		
        		performanceInfo += "\n"+projectName+" in ms:"+(System.currentTimeMillis()-startProject);
        		
        		vr.getTimePeriodReports().add(tpr);
            }
            
            //reset today's date so that the next call reinitializes the date (was bug #2)
            today=null;
            
            if(mode.equals("byWeek")){
            	ihrByWeek.addPerformanceInformation(performanceInfo);
            	ihrByWeek.addPerformanceInformation("\nTotal time in ms:"+(System.currentTimeMillis()-startTime));
            	return Response.ok(ihrByWeek).build();
            }
            else{
            	ihrByWeek.addPerformanceInformation(performanceInfo);
            	ihrByWeek.addPerformanceInformation("\nTotal time in ms:"+(System.currentTimeMillis()-startTime));
                return Response.ok(ihr).build();
            }
        }
    	catch(Exception e){
    		IndividualHoursReport ihr = new IndividualHoursReport(userIdString, new ArrayList<ProjectReport>(),Arrays.toString(e.getStackTrace()),"");
    		ihr.addPerformanceInformation(performanceInfo);
        	ihr.addPerformanceInformation("\nTotal time in ms:"+(System.currentTimeMillis()-startTime));
    		return Response.ok(ihr).build();
        }
    }

    /**
     * Determines if the project has a dealine on the current date
     * @param projectName
     * @param currentDate
     * @return
     */
    private Boolean hasDeadline(String projectName, Date currentDate) {
		
    	if(getDeadline(projectName, currentDate)==null){
    		return false;
    	}
    	else{
    		return true;
    	}
	}
    
    /**
     * Determines if the project has a dealine on the current date or up to one week earlier
     * @param projectName
     * @param currentDate
     * @return
     */
    private Boolean hasDeadlineInWeek(String projectName, Date currentDate) {
		
    	if(getDeadlineInWeek(projectName, currentDate)==null){
    		return false;
    	}
    	else{
    		return true;
    	}
	}
    
    /**
     * Determines if the project has a deadline on the current date. Returns null if the currentDate is in the past (yesterday)
     * @param projectName
     * @param currentDate
     * @return
     */
    private String getDeadline(String projectName, Date currentDate) {
		
    	//set date to beginning of the day
    	currentDate = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
    	
    	String result = null;
    	
    	if(currentDate.before(today())){
    		return null;
    	}
    	
    	Project p = projectManager.getProjectObjByKey(projectName);
		if(p!=null){
			for(Version v : p.getVersions()){
				if(v.getReleaseDate()!=null
						&& v.getReleaseDate().getYear()==currentDate.getYear()
						&& v.getReleaseDate().getMonth()==currentDate.getMonth()
						&& v.getReleaseDate().getDate()==currentDate.getDate()){
					if(result==null)
						result=v.getName();
					else
						result+=","+v.getName(); //accounts for multiple deadlines on one day
				}
			}
		}
		return result;
	}
    
    /**
     * Determines if the project has a deadline on the current date or up to one week earlier. Returns null if the currentDate is in the past (yesterday)
     * @param projectName
     * @param currentDate
     * @return
     */
    private String getDeadlineInWeek(String projectName, Date currentDate) {
		
    	//set date to beginning of the day
    	currentDate = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
    	
    	String result = null;
    	
    	if(currentDate.before(today())){
    		return null;
    	}
    	
    	Project p = projectManager.getProjectObjByKey(projectName);
		if(p!=null){
			for(Version v : p.getVersions()){
				if(v.getReleaseDate()==null) continue;
				Date releaseDate = new Date(v.getReleaseDate().getYear(), v.getReleaseDate().getMonth(), v.getReleaseDate().getDate());
				if(    
						(releaseDate.before(currentDate) && releaseDate.after(new Date(currentDate.getTime()-6*24*60*60*1000)))
						|| releaseDate.equals(currentDate) || releaseDate.equals(new Date(currentDate.getTime()-6*24*60*60*1000)))
				
				{
					if(result==null)
						result=v.getName();
					else
						result+=","+v.getName(); //accounts for multiple deadlines on one day
				}
			}
		}
		return result;
	}


	/**
     * Formats the date output to : 5/27
     * if date is before today: "pending"
     * @param date
     * @return
     */
    private String formatDate(Date date) {
		if(date.before(today())){
			return "pending";
		}
		else{
			return (""+(date.getMonth()+1)+"/"+(date.getDate()));
		}
    }
    
    /**
     * Formats the date output to : 5/14 - 5/18
     * Assumes that the provided day is the last day, the Sunday of the week 
     * if date is before today: "prending"
     * @param date
     * @return
     */
    private String formatDateWeek(Date date) {
		if(date.before(today())){
			return "pending";
		}
		else{
			//last Monday
			Date monday = new Date(date.getTime()-6*24*60*60*1000);
			//last Friday
			Date friday = new Date(date.getTime()-2*24*60*60*1000);
			
			//return (""+(monday.getMonth()+1)+"/"+(monday.getDate())+" - "+(friday.getMonth()+1)+"/"+(friday.getDate()));
			return (""+(monday.getMonth()+1)+"/"+(monday.getDate()));
		}
    }


    /**
     * Returns today's date
     * @return
     */
	private Date today() {
		if(today==null){
			today=new Date(new Date().getYear(), new Date().getMonth(), new Date().getDate());
		}
		return today;
	}


	/**
     * Computes the remaining time estimate for all open issues that are due (by fix version due date) after date if date is today or in the future.
     * If day is yesterday, or earlier, it computes the remaining time of all open issues that due date is in the past.
     * If day is null, computes sum of all estimates for issue that are "unscheduled" (i.e. have no fix version with date, and no due date)
     * @param projectName
     * @param j
     * @param issues
     * @return
     */
    private Double computeRemainingEstimateByDay(String projectName, Date day,
			List<Issue> issues, List<String> issueTypes, Double defaultRemainingTime, ArrayList<TimePeriodReportDetail> reportDetail) {
		
    	
    	//set date to beginning of the day
    	if(day!=null){
    		day = new Date(day.getYear(), day.getMonth(), day.getDate());
    	}
    	
    	
    	Date today = today();
    	
    	//number of remaining work for that day
    	Double numberOfRemainingHours = 0.0;
    	
    	//if weekend then return 0.0h
    	//TODO: make this more flexible, so that weekend work can be considered, not all countries will have Saturday and Sunday weekends
		if(day != null && (day.getDay()==0 || day.getDay()==6)){
			return 0.0;
		}
    	
    	
    	for(Issue i : issues){
    		
    		//this is the issue's due date either determined by the fix version (preferred) or due date
    		Date releaseDate = null;
    		Version fixVersion = null;
    		//take the first fix version
        	//TODO: what if an issue has multiple fix versions assigned?
    		for(Version v : i.getFixVersions()){
    			if(v.getReleaseDate()!=null){
    				releaseDate=v.getReleaseDate();
    				fixVersion=v;
    				break;
        		}
    			else{
    				//if fix version assigned, but no date set, then go to next fixVersion
    				continue;
    			}
    		}
    		
    		//if issue does not have a fix version with date (ergo has no release date assigned yet), then check for a due date
    		if(releaseDate == null){
    			if(i.getDueDate()!=null){
    				releaseDate = i.getDueDate();
    			}
    			//not even a due date assigned, then ignore
    			else{
    				if (day!=null){
    					continue; //if no fix version assigned and no due date assigned ignore this issue, unless the special case where we are looking for unscheduled issues
    				}
    			}
    		}
    		
    		//check for right issue type according to gadget settings
    		if(! issueTypes.contains(i.getIssueTypeObject().getName()) && issueTypes.size()>0) continue; //not the right issue type, unless the list is empty (wildcard for all types)
    		
    		
    		//compute remaining estimate for that day
    		Double remainingEstimate;
    		
        	if(i.getEstimate()!=null){
        		remainingEstimate=new Double(i.getEstimate())/3600.0;
        	}
        	else{
        		//add one hour for tasks without estimate
        		remainingEstimate=defaultRemainingTime;
        	}
        	
        	//unscheduled
    		if(day==null && releaseDate == null){
				numberOfRemainingHours += remainingEstimate ;
				TimePeriodReportDetail detail = new TimePeriodReportDetail(i.getKey(), "unscheduled", fixVersion!=null?fixVersion.getName():"-", remainingEstimate,remainingEstimate,i.getSummary());
				reportDetail.add(detail);
    		}
			else if(day!=null){
				// current date after of same day as release date
				if( ! day.after(releaseDate)  && ! day.before(today)){
					//number of available workdays between today and the release date (both inclusive)
    				Long numberOfWorkdays = computeNumberOfWorkdays(today,releaseDate); 
    				//null in case the release date is prior to current date
    				if(numberOfWorkdays!=null && numberOfWorkdays >0){
    					Double time = new Double(remainingEstimate)/new Double(numberOfWorkdays);
    					numberOfRemainingHours += time;
    					TimePeriodReportDetail detail = new TimePeriodReportDetail(i.getKey(), formatDate(releaseDate), fixVersion!=null?fixVersion.getName():"-", time,remainingEstimate,i.getSummary());
    					reportDetail.add(detail);
    				}
    			}
    			else if(day.before(today) && releaseDate.before(today)){
    				////pending issues
    				numberOfRemainingHours += remainingEstimate;
    				TimePeriodReportDetail detail = new TimePeriodReportDetail(i.getKey(), formatDate(releaseDate), fixVersion!=null?fixVersion.getName():"-", remainingEstimate,remainingEstimate,i.getSummary());
					reportDetail.add(detail);
    			}
			}
		
    	}
    	
    	return numberOfRemainingHours;
	}


	/**
     * Simply iterates through issues and return the list of projects keys (without duplicates) the issues are assigned to
     * @param issues
     * @return
     */
    private ArrayList<String> getAllProjectNames(List<Issue> issues) {
		ArrayList<String> result = new ArrayList<String>();
		for(Issue i : issues){
			if(! result.contains(i.getProjectObject().getKey())){
				result.add(i.getProjectObject().getKey());
			}
		}
		return result;
	}


	/**
     * Computes the number of work days (Mon-Fri) between two dates (both days are included as work days)
     * 
     * Returns null if the release date is before the current date.
     * 
     * @param date : assumes start of day
     * @param releaseDate
     * @return
     */
	private Long computeNumberOfWorkdays(Date currentDate, Date releaseDate) {
		Long days = 0L;
		
		
		if(releaseDate.before(currentDate))
				return null;

		while (! currentDate.after(releaseDate)){
			//if currentDate is not Sun or Sat
			if(!(currentDate.getDay()==0 || currentDate.getDay()==6)){
				days++;
			}
			//add one day
			currentDate = new Date(currentDate.getTime()+(24*60*60*1000));
		}
		
		return days;
	}
	
	@XmlRootElement
	public static class IndividualHoursReport {
		
		@XmlElement
		private String userName;
		
		@XmlElement
		Collection<ProjectReport> projectReports;
		
		@XmlElement
		private String errorMessage;
		
		@XmlElement
		private String performanceInformation;
		
		IndividualHoursReport(String userName, Collection<ProjectReport> projectReports, String errorMessage, String performanceInformation){
			this.userName=userName;
			this.projectReports=projectReports;
			this.errorMessage=errorMessage;
			this.performanceInformation = performanceInformation;
		}
		
		private IndividualHoursReport(){}
		
		public String getUserName(){
			return userName;
		}
		
		public Collection<ProjectReport> getProjectReports(){
			return projectReports;
		}
		
		public String getErrorMessage(){
			return errorMessage;
		}
		
		public String getPerformanceInformation(){
			return performanceInformation;
		}
		
		public void addPerformanceInformation(String newInformation){
			performanceInformation+=newInformation;
		}
		
	}
	
	@XmlRootElement
	public static class ProjectReport{
		
		@XmlElement
		private String projectName;
		
		@XmlElement
		private Collection<VersionReport> versionReports;
		
		ProjectReport(String projectName, Collection<VersionReport> versionReports){
			this.projectName=projectName;
			this.versionReports=versionReports;
		}
		
		private ProjectReport(){}
		
		public String getProjectName(){
			return projectName;
		}
		
		public Collection<VersionReport> getVersionReports(){
			return versionReports;
		}
		
	}
	
	@XmlRootElement
	public static class VersionReport{
		@XmlElement
		private String versionName;
		
		@XmlElement
		private Collection<TimePeriodReport> timePeriodReports;
		
		VersionReport(String versionName, Collection<TimePeriodReport> timePeriodReports){
			this.versionName=versionName;
			this.timePeriodReports=timePeriodReports;
		}
		
		private VersionReport(){}
		
		public String getVersionName(){
			return versionName;
		}
		
		public Collection<TimePeriodReport> getTimePeriodReports(){
			return timePeriodReports;
		}
	}
	
	@XmlRootElement
	public static class TimePeriodReport{
		
		@XmlElement
		private Double allocatedHours;
		
		@XmlElement
		private String label;
		
		@XmlElement
		private Boolean hasVersionDeadline;
		
		@XmlElement
		private Collection<TimePeriodReportDetail> reportDetails;
		
		/**
		 * holds the version name, if a version has a deadline in this time period
		 */
		@XmlElement
		private String deadlineVersion;
		
		/**
		 * greyed out for weekend days
		 */
		@XmlElement
		private Boolean greyedOut;
		
		
		TimePeriodReport(Double allocatedHours, String label, Boolean hasVersionDeadline, String deadlineVersion, Boolean greyedOut, Collection<TimePeriodReportDetail> reportDetails){
			this.allocatedHours=allocatedHours;
			this.label=label;
			this.hasVersionDeadline=hasVersionDeadline;
			this.deadlineVersion = deadlineVersion;
			this.greyedOut = greyedOut;
			this.reportDetails = reportDetails;
		}
		
		private TimePeriodReport(){}
		
		public Double getAllocatedHours(){
			return allocatedHours;
		}
		
		public Boolean getHasVersionDeadline(){
			return hasVersionDeadline;
		}
		
		public String getLabel(){
			return label;
		}
		
		public String getDeadlineVersion(){
			return deadlineVersion;
		}
		
		public Boolean getGreyedOut(){
			return greyedOut;
		}
		
		public Collection<TimePeriodReportDetail> getReportDetails(){
			return reportDetails;
		}
		
		public void addTimePeriodReportDetail(TimePeriodReportDetail detail){
			reportDetails.add(detail);
		}
	}
	
	@XmlRootElement
	public static class TimePeriodReportDetail{
		
		@XmlElement
		private String jiraIssueKey;
		
		@XmlElement
		private String dueDate;
		
		@XmlElement
		private String fixVersion;
		
		/**
		 * Allocated Hours for a time period (e.g. week or day)
		 */
		@XmlElement
		private Double allocatedHours;
		
		/**
		 * Total remaining allocated hours
		 */
		@XmlElement
		private Double totalAllocatedHours;
		
		@XmlElement
		private String summary;
		
		TimePeriodReportDetail(String jiraIssueKey, String dueDate, String fixVersion, Double allocatedHours, Double totalAllocatedHours, String summary){
			this.jiraIssueKey=jiraIssueKey;
			this.dueDate=dueDate;
			this.fixVersion=fixVersion;
			this.allocatedHours=allocatedHours;
			this.totalAllocatedHours=totalAllocatedHours;
			this.summary=summary;
			
		}
		
		private TimePeriodReportDetail(){}

		public void addAllocatedHours(Double allocatedHours2) {
			this.allocatedHours += allocatedHours2;
			
		}

		public String getJiraIssueKey() {
			return jiraIssueKey;
		}

		public String getDueDate() {
			return dueDate;
		}

		public String getFixVersion() {
			return fixVersion;
		}

		public Double getAllocatedHours() {
			return allocatedHours;
		}
		
		public Double getTotalAllocatedHours(){
			return totalAllocatedHours;
		}
		
		public String getSummary(){
			return summary;
		}
		
		
		
	}
}